import * as Cesium from 'cesium'
import { TransformHelper } from './transformHelper/TransformHelper'
class Viewer {
  viewer: Cesium.Viewer | undefined
  constructor(
    container: Element | string,
    option: Cesium.Viewer.ConstructorOptions
  ) {
    this.init(container, option)
  }
  async init(
    container: Element | string,
    option: Cesium.Viewer.ConstructorOptions
  ) {
    this.initViewer(container, option)
  }
  // 初始化地球
  async initViewer(
    container: Element | string,
    option: Cesium.Viewer.ConstructorOptions
  ) {
    this.viewer = new Cesium.Viewer(container, {
      //是否创建动画小器件，左下角仪表
      animation: false,
      //是否显示全屏按钮
      fullscreenButton: false,
      //放大镜图标，查找位置工具，查找到之后会将镜头对准找到的地址，默认使用bing地图
      geocoder: false,
      //房子图标，是否显示Home按钮，视角返回初始位置
      homeButton: false,
      //是否显示信息框
      infoBox: false,
      //经纬网图标，选择视角的模式，有三种：3D，2D，哥伦布视图（2.5D)，是否显示3D/2D选择器
      sceneModePicker: false,
      //是否显示时间轴
      timeline: false,
      //设定3维地图的默认场景模式:Cesium.SceneMode.SCENE2D、Cesium.SceneMode.SCENE3D、Cesium.SceneMode.MORPHING
      sceneMode: Cesium.SceneMode.SCENE3D,
      //如果设置为true，则所有几何图形以3D模式绘制以节约GPU资源
      scene3DOnly: true,
      //是否显示图层选择器，可选择要显示的地图服务和地形服务
      baseLayerPicker: false,
      //问号图标，右上角导航帮助按钮，显示默认的地图控制帮助
      navigationHelpButton: false,
      //虚拟现实
      vrButton: false,
      //是否显示选取指示器组件
      selectionIndicator: false,
      //设置背景透明,涉及透贴显示
      orderIndependentTranslucency: true,
      //关闭默认底图以支持离线使用
      baseLayer: false,
      contextOptions: {
        webgl: {
          preserveDrawingBuffer: false, //通过canvas.toDataURL()实现截图需要将该项设置为true
          failIfMajorPerformanceCaveat: true,
        },
        requestWebgl1: true,
      },
      ...option,
    })

    //隐藏版本信息
    ;(this.viewer.cesiumWidget.creditContainer as any).style.display = 'none'

    //默认打开深度检测，那么在地形以下的对象不可见
    this.viewer.scene.globe.depthTestAgainstTerrain = true
    //是否显示星空
    // this.viewer.scene.skyBox.show = true
    //是否显示太阳
    // this.viewer.scene.sun.show = true
    //是否显示有月亮
    this.viewer.scene.moon.show = false
    //是否隐藏大气层
    // this.viewer.scene.skyAtmosphere.show = true
    //关闭全球光照
    // this.viewer.scene.globe.enableLighting = true
    //阴影
    // this.viewer.shadows = true

    //禁止相机进入地下 false允许，true禁止
    this.viewer.scene.screenSpaceCameraController.enableCollisionDetection =
      false
  }
  // 操作对齐mapbox
  asMapboxControl() {
    if (!this.viewer) return
    const { scene } = this.viewer
    scene.screenSpaceCameraController.tiltEventTypes = [
      Cesium.CameraEventType.RIGHT_DRAG,
      Cesium.CameraEventType.PINCH,
      {
        eventType: Cesium.CameraEventType.LEFT_DRAG,
        modifier: Cesium.KeyboardEventModifier.CTRL,
      },
      {
        eventType: Cesium.CameraEventType.RIGHT_DRAG,
        modifier: Cesium.KeyboardEventModifier.CTRL,
      },
    ]
    scene.screenSpaceCameraController.zoomEventTypes = [
      Cesium.CameraEventType.MIDDLE_DRAG,
      Cesium.CameraEventType.WHEEL,
      Cesium.CameraEventType.PINCH,
    ]
  }
  //获取精确贴地形高度
  async getClampHeight({
    viewer,
    lnglat,
  }: {
    viewer: Cesium.Viewer
    lnglat: [number, number]
  }) {
    const terrainProvider = viewer.scene.terrainProvider
    const positions = [Cesium.Cartographic.fromDegrees(lnglat[0], lnglat[1])]
    const updatedPositions = await Cesium.sampleTerrainMostDetailed(
      terrainProvider,
      positions
    )
    return updatedPositions[0].height
  }
  //加载模型
  async loadModel({
    viewer,
    position: [lng, lat, height = 0],
    modelUrl,
    bearing = 0,
    id,
    clampToGround = false,
  }: {
    viewer: Cesium.Viewer
    position: [number, number, number]
    modelUrl: string
    bearing: number
    id: string
    clampToGround?: boolean
  }) {
    let heightParam = height
    if (clampToGround) {
      //获取精确贴地形高度
      const terrainProvider = viewer.scene.terrainProvider
      const positions = [Cesium.Cartographic.fromDegrees(lng, lat)]
      const updatedPositions = await Cesium.sampleTerrainMostDetailed(
        terrainProvider,
        positions
      )
      heightParam = updatedPositions[0].height
    }

    // 将经纬度转换为笛卡尔坐标
    const position = Cesium.Cartesian3.fromDegrees(lng, lat, heightParam)
    // 计算模型的朝向
    const offsetBearing = 180
    const heading = Cesium.Math.toRadians(bearing + offsetBearing) // 将角度转换为弧度
    const pitch = 0 // 模型的俯仰角
    const roll = 0 // 模型的翻滚角
    const headingPositionRoll = new Cesium.HeadingPitchRoll(
      heading,
      pitch,
      roll
    )
    const fixedFrameTransform =
      Cesium.Transforms.localFrameToFixedFrameGenerator('north', 'west')

    // 将旋转矩阵和位置矩阵组合成模型矩阵
    const modelMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(
      position,
      headingPositionRoll,
      Cesium.Ellipsoid.WGS84,
      fixedFrameTransform
    )
    // this.addLabel(`${index}`, [lng, lat, null]);
    // 添加实体并设置模型

    const entity = viewer.scene.primitives.add(
      await Cesium.Model.fromGltfAsync({
        id,
        url: modelUrl,
        show: true, // default
        scale: 0.8, // double size
        allowPicking: true,
        modelMatrix: modelMatrix,
      })
    )
    entity.imageBasedLighting.luminanceAtZenith = 0.4
    return entity
  }

  //调整3dtiles高度
  adjust3DTilesetHeight(tileset: Cesium.Cesium3DTileset, height: number) {
    if (!tileset || typeof height !== 'number') {
      console.error('Invalid tileset or height')
      return
    }

    //1.获取原始坐标
    const originalCenter = tileset.boundingSphere.center

    //2.计算高度调整height米后的坐标;
    const originalCarPoint = Cesium.Cartographic.fromCartesian(originalCenter)
    originalCarPoint.height += height
    const { longitude, latitude, height: alt } = originalCarPoint
    const adjustedCenter = Cesium.Cartesian3.fromRadians(
      longitude,
      latitude,
      alt
    )

    //3. 从起始点和终点计算平移向量
    const translationVector = Cesium.Cartesian3.subtract(
      adjustedCenter,
      originalCenter,
      new Cesium.Cartesian3()
    )

    //4. 计算平移变换矩阵
    const transform = Cesium.Matrix4.fromTranslation(translationVector)

    //5. 原矩阵乘以变换矩阵并应用到模型的modelMatrix
    const finalMatrix = Cesium.Matrix4.multiply(
      transform,
      tileset.modelMatrix,
      new Cesium.Matrix4()
    )
    tileset.modelMatrix = finalMatrix
  }

  //multipolygon转gml
  multiPolygonToGml(
    multiPolygon: GeoJSON.Feature<GeoJSON.MultiPolygon>,
    srsCode?: number
  ) {
    const multipolygon = multiPolygon.geometry.coordinates
    console.log(multiPolygon, '格式转换')
    const srsCodeParam = srsCode ? srsCode : 4326
    // Create GML structure
    let gmlXml = `<gml:MultiPolygon srsName="EPSG:${srsCodeParam}">\n`

    // Iterate over each polygon in the multipolygon
    multipolygon.forEach((polygonCoords: any) => {
      gmlXml += `  <gml:polygonMember>\n`
      gmlXml += `    <gml:Polygon>\n`
      gmlXml += `      <gml:exterior>\n`
      gmlXml += `        <gml:LinearRing>\n`
      gmlXml += `          <gml:coordinates>`

      // Convert coordinates to GML format
      const coordinates = polygonCoords[0]
        .map((coord: number[]) => `${coord[0]},${coord[1]}`)
        .join(' ')

      gmlXml += `${coordinates}</gml:coordinates>\n`
      gmlXml += `        </gml:LinearRing>\n`
      gmlXml += `      </gml:exterior>\n`
      gmlXml += `    </gml:Polygon>\n`
      gmlXml += `  </gml:polygonMember>\n`
    })

    gmlXml += `</gml:MultiPolygon>\n`

    console.log(gmlXml, '格式转换')

    return gmlXml
  }
}

export { Viewer, TransformHelper }
